home *** CD-ROM | disk | FTP | other *** search
/ Cream of the Crop 26 / Cream of the Crop 26.iso / doom / quake.zip / HIPGRAPL.ZIP / HIPROT.QC < prev    next >
Text File  |  1997-02-10  |  27KB  |  1,124 lines

  1. /* Rotate QuickC program
  2.    By Jim Dose'  10/17/96
  3.    Copyright (c)1996 Hipnotic Interactive, Inc.
  4.    All rights reserved.
  5.    Do not distribute.
  6. */
  7.  
  8. float STATE_ACTIVE      = 0;
  9. float STATE_INACTIVE    = 1;
  10. float STATE_SPEEDINGUP  = 2;
  11. float STATE_SLOWINGDOWN = 3;
  12.  
  13. float STATE_CLOSED   = 4;
  14. float STATE_OPEN     = 5;
  15. float STATE_OPENING  = 6;
  16. float STATE_CLOSING  = 7;
  17.  
  18. float STATE_WAIT = 0;
  19. float STATE_MOVE = 1;
  20. float STATE_STOP = 2;
  21. float STATE_FIND = 3;
  22. float STATE_NEXT = 4;
  23.  
  24. float OBJECT_ROTATE = 0;
  25. float OBJECT_MOVEWALL  = 1;
  26. float OBJECT_SETORIGIN = 2;
  27.  
  28. float TOGGLE   = 1;
  29. float START_ON = 2;
  30.  
  31. float ROTATION = 1;
  32. float ANGLES   = 2;
  33. float STOP     = 4;
  34. float NO_ROTATE = 8;
  35. float DAMAGE   = 16;
  36. float MOVETIME = 32;
  37. float SET_DAMAGE = 64;
  38.  
  39. float VISIBLE = 1;
  40. float TOUCH = 2;
  41. float NONBLOCKING = 4;
  42.  
  43. float STAYOPEN = 1;
  44.  
  45. /*QUAKED info_rotate (0 0.5 0) (-4 -4 -4) (4 4 4)
  46. Used as the point of rotation for rotatable objects.
  47. */
  48. void() info_rotate =
  49. {
  50. // remove self after a little while, to make sure that entities that
  51. // have targeted it have had a chance to spawn
  52.    self.nextthink = time + 2;
  53.    self.think = SUB_Remove;
  54. };
  55.  
  56. void() RotateTargets =
  57.    {
  58.    local entity ent;
  59.    local vector vx;
  60.    local vector vy;
  61.    local vector vz;
  62.    local vector org;
  63.  
  64.    makevectors (self.angles);
  65.  
  66.    ent = find( world, targetname, self.target);
  67.    while( ent )
  68.       {
  69.       if ( ent.rotate_type == OBJECT_SETORIGIN )
  70.          {
  71.          org = ent.oldorigin;
  72.          vx = ( v_forward * org_x );
  73.          vy = ( v_right   * org_y );
  74.          vy = vy * -1;
  75.          vz = ( v_up      * org_z );
  76.          ent.neworigin = vx + vy + vz;
  77.          setorigin( ent, ent.neworigin + self.origin );
  78.          }
  79.       else if ( ent.rotate_type == OBJECT_ROTATE )
  80.          {
  81.          ent.angles = self.angles;
  82.          org = ent.oldorigin;
  83.          vx = ( v_forward * org_x );
  84.          vy = ( v_right   * org_y );
  85.          vy = vy * -1;
  86.          vz = ( v_up      * org_z );
  87.          ent.neworigin = vx + vy + vz;
  88.          setorigin( ent, ent.neworigin + self.origin );
  89.          }
  90.       else
  91.          {
  92.          org = ent.oldorigin;
  93.          vx = ( v_forward * org_x );
  94.          vy = ( v_right   * org_y );
  95.          vy = vy * -1;
  96.          vz = ( v_up      * org_z );
  97.          ent.neworigin = vx + vy + vz;
  98.          ent.neworigin = self.origin - self.oldorigin + (ent.neworigin - ent.oldorigin);
  99.          ent.velocity = (ent.neworigin-ent.origin)*25;
  100.          }
  101.       ent = find( ent, targetname, self.target);
  102.       }
  103.    };
  104.  
  105. void() RotateTargetsFinal =
  106.    {
  107.    local entity ent;
  108.  
  109.    ent = find( world, targetname, self.target);
  110.    while( ent )
  111.       {
  112.       ent.velocity = '0 0 0';
  113.       if ( ent.rotate_type == OBJECT_ROTATE )
  114.          {
  115.          ent.angles = self.angles;
  116.          }
  117.       ent = find( ent, targetname, self.target);
  118.       }
  119.    };
  120.  
  121. void() SetTargetOrigin =
  122.    {
  123.    local entity ent;
  124.  
  125.    ent = find( world, targetname, self.target);
  126.    while( ent )
  127.       {
  128.       if ( ent.rotate_type == OBJECT_MOVEWALL )
  129.          {
  130.          setorigin( ent, self.origin - self.oldorigin +
  131.             (ent.neworigin - ent.oldorigin) );
  132.          }
  133.       else
  134.          {
  135.          setorigin( ent, ent.neworigin + self.origin );
  136.          }
  137.       ent = find( ent, targetname, self.target);
  138.       }
  139.    };
  140.  
  141. void() LinkRotateTargets =
  142.    {
  143.    local entity ent;
  144.    local vector tempvec;
  145.  
  146.    self.oldorigin = self.origin;
  147.    ent = find( world, targetname, self.target);
  148.    while( ent )
  149.       {
  150.       if ( ent.classname == "rotate_object" )
  151.          {
  152.          ent.rotate_type = OBJECT_ROTATE;
  153.          ent.oldorigin = ent.origin - self.oldorigin;
  154.          ent.neworigin = ent.origin - self.oldorigin;
  155.          ent.owner = self;
  156.          }
  157.       else if ( ent.classname == "func_movewall" )
  158.          {
  159.          ent.rotate_type = OBJECT_MOVEWALL;
  160.          tempvec = ( ent.absmin + ent.absmax ) * 0.5;
  161.          ent.oldorigin = tempvec - self.oldorigin;
  162.          ent.neworigin = ent.oldorigin;
  163.          ent.owner = self;
  164.          }
  165.       else
  166.          {
  167.          ent.rotate_type = OBJECT_SETORIGIN;
  168.          ent.oldorigin = ent.origin - self.oldorigin;
  169.          ent.neworigin = ent.origin - self.oldorigin;
  170.          }
  171.       ent = find (ent, targetname, self.target);
  172.       }
  173.    };
  174.  
  175. void( float amount ) SetDamageOnTargets =
  176.    {
  177.    local entity ent;
  178.  
  179.    ent = find( world, targetname, self.target);
  180.    while( ent )
  181.       {
  182.       if ( ent.classname == "trigger_hurt" )
  183.          {
  184.          hurt_setdamage( ent, amount );
  185.          }
  186.       else if ( ent.classname == "func_movewall" )
  187.          {
  188.          ent.dmg = amount;
  189.          }
  190.       ent = find( ent, targetname, self.target);
  191.       }
  192.    };
  193.  
  194.  
  195. //************************************************
  196. //
  197. // Simple continual rotatation
  198. //
  199. //************************************************
  200.  
  201. void() rotate_entity_think =
  202.    {
  203.    local float t;
  204.  
  205.    t = time - self.ltime;
  206.    self.ltime = time;
  207.  
  208.    if ( self.state == STATE_SPEEDINGUP )
  209.       {
  210.       self.count = self.count + self.cnt * t;
  211.       if ( self.count > 1 )
  212.          {
  213.          self.count = 1;
  214.          }
  215.  
  216.       // get rate of rotation
  217.       t = t * self.count;
  218.       }
  219.    else if ( self.state == STATE_SLOWINGDOWN )
  220.       {
  221.       self.count = self.count - self.cnt * t;
  222.       if ( self.count < 0 )
  223.          {
  224.          RotateTargetsFinal();
  225.          self.state = STATE_INACTIVE;
  226.          self.think = SUB_Null;
  227.          return;
  228.          }
  229.  
  230.       // get rate of rotation
  231.       t = t * self.count;
  232.       }
  233.  
  234.    self.angles = self.angles + ( self.rotate * t );
  235.    self.angles = SUB_NormalizeAngles( self.angles );
  236.    RotateTargets();
  237.    self.nextthink = time + 0.02;
  238.    };
  239.  
  240. void() rotate_entity_use =
  241.    {
  242.    // change to alternate textures
  243.     self.frame = 1 - self.frame;
  244.  
  245.    if ( self.state == STATE_ACTIVE )
  246.       {
  247.       if ( self.spawnflags & TOGGLE )
  248.          {
  249.          if ( self.speed )
  250.             {
  251.             self.count = 1;
  252.             self.state = STATE_SLOWINGDOWN;
  253.             }
  254.          else
  255.             {
  256.             self.state = STATE_INACTIVE;
  257.             self.think = SUB_Null;
  258.             }
  259.          }
  260.       }
  261.    else if ( self.state == STATE_INACTIVE )
  262.       {
  263.       self.think = rotate_entity_think;
  264.       self.nextthink = time + 0.02;
  265.       self.ltime = time;
  266.       if ( self.speed )
  267.          {
  268.          self.count = 0;
  269.          self.state = STATE_SPEEDINGUP;
  270.          }
  271.       else
  272.          {
  273.          self.state = STATE_ACTIVE;
  274.          }
  275.       }
  276.    else if ( self.state == STATE_SPEEDINGUP )
  277.       {
  278.       if ( self.spawnflags & TOGGLE )
  279.          {
  280.          self.state = STATE_SLOWINGDOWN;
  281.          }
  282.       }
  283.    else
  284.       {
  285.       self.state = STATE_SPEEDINGUP;
  286.       }
  287.    };
  288.  
  289. void() rotate_entity_firstthink =
  290.    {
  291.    LinkRotateTargets();
  292.    if ( self.spawnflags & START_ON )
  293.       {
  294.       self.state = STATE_ACTIVE;
  295.       self.think = rotate_entity_think;
  296.       self.nextthink = time + 0.02;
  297.       self.ltime = time;
  298.       }
  299.    else
  300.       {
  301.       self.state = STATE_INACTIVE;
  302.       self.think = SUB_Null;
  303.       }
  304.    self.use = rotate_entity_use;
  305.    };
  306.  
  307. /*QUAKED func_rotate_entity (0 .5 .8) (-8 -8 -8) (8 8 8) TOGGLE START_ON
  308. Creates an entity that continually rotates.  Can be toggled on and
  309. off if targeted.
  310.  
  311. TOGGLE = allows the rotation to be toggled on/off
  312.  
  313. START_ON = wether the entity is spinning when spawned.  If TOGGLE is 0, entity can be turned on, but not off.
  314.  
  315. If "deathtype" is set with a string, this is the message that will appear when a player is killed by the train.
  316.  
  317. "rotate" is the rate to rotate.
  318. "target" is the center of rotation.
  319. "speed"  is how long the entity takes to go from standing still to full speed and vice-versa.
  320. */
  321.  
  322. void() func_rotate_entity =
  323.    {
  324.    self.solid    = SOLID_NOT;
  325.    self.movetype = MOVETYPE_NONE;
  326.  
  327.    setmodel (self, self.model);
  328.    setsize( self, self.mins, self.maxs );
  329.  
  330.    if ( self.speed != 0 )
  331.       {
  332.       self.cnt = 1 / self.speed;
  333.       }
  334.  
  335.    self.think = rotate_entity_firstthink;
  336.    self.nextthink = time + 0.1;
  337.    self.ltime = time;
  338.    };
  339.  
  340. //************************************************
  341. //
  342. // Train with rotation functionality
  343. //
  344. //************************************************
  345.  
  346. /*QUAKED path_rotate (0.5 0.3 0) (-8 -8 -8) (8 8 8) ROTATION ANGLES STOP NO_ROTATE DAMAGE MOVETIME SET_DAMAGE
  347.  Path for rotate_train.
  348.  
  349.  ROTATION tells train to rotate at rate specified by "rotate".  Use '0 0 0' to stop rotation.
  350.  
  351.  ANGLES tells train to rotate to the angles specified by "angles" while traveling to this path_rotate.  Use values < 0 or > 360 to guarantee that it turns in a certain direction.  Having this flag set automatically clears any rotation.
  352.  
  353.  STOP tells the train to stop and wait to be retriggered.
  354.  
  355.  NO_ROTATE tells the train to stop rotating when waiting to be triggered.
  356.  
  357.  DAMAGE tells the train to cause damage based on "dmg".
  358.  
  359.  MOVETIME tells the train to interpret "speed" as the length of time to take moving from one corner to another.
  360.  
  361.  SET_DAMAGE tells the train to set all targets damage to "dmg"
  362.  
  363.  "noise" contains the name of the sound to play when train stops.
  364.  "noise1" contains the name of the sound to play when train moves.
  365.  "event" is a target to trigger when train arrives at path_rotate.
  366. */
  367. void() path_rotate =
  368.    {
  369.    if ( self.noise )
  370.       {
  371.       precache_sound( self.noise );
  372.       }
  373.    if ( self.noise1 )
  374.       {
  375.       precache_sound( self.noise1 );
  376.       }
  377.    };
  378.  
  379.  
  380. void() rotate_train;
  381. void() rotate_train_next;
  382. void() rotate_train_find;
  383.  
  384. void() rotate_train_think =
  385.    {
  386.    local float t;
  387.    local float timeelapsed;
  388.  
  389.    t = time - self.ltime;
  390.    self.ltime = time;
  391.  
  392.    if ( ( self.endtime ) && ( time >= self.endtime ) )
  393.       {
  394.       self.endtime = 0;
  395.       if ( self.state == STATE_MOVE )
  396.          {
  397.          setorigin(self, self.finaldest);
  398.          self.velocity = '0 0 0';
  399.          }
  400.  
  401.       if (self.think1)
  402.          self.think1();
  403.       }
  404.    else
  405.       {
  406.       timeelapsed = (time - self.cnt) * self.duration;
  407.       if ( timeelapsed > 1 )
  408.          timeelapsed = 1;
  409.       setorigin( self, self.dest1 + ( self.dest2 * timeelapsed ) );
  410.       }
  411.  
  412.    self.angles = self.angles + ( self.rotate * t );
  413.    self.angles = SUB_NormalizeAngles( self.angles );
  414.    RotateTargets();
  415.  
  416.    self.nextthink = time + 0.02;
  417.    };
  418.  
  419. void() rotate_train_use =
  420.    {
  421.    if (self.think1 != rotate_train_find)
  422.         {
  423.         if ( self.velocity != '0 0 0' )
  424.             return;        // already activated
  425.       if ( self.think1 )
  426.          {
  427.          self.think1();
  428.          }
  429.       }
  430.    };
  431.  
  432. void() rotate_train_wait =
  433.     {
  434.    self.state = STATE_WAIT;
  435.  
  436.    if ( self.goalentity.noise )
  437.       {
  438.       sound (self, CHAN_VOICE, self.goalentity.noise, 1, ATTN_NORM);
  439.       }
  440.    else
  441.       {
  442.       sound (self, CHAN_VOICE, self.noise, 1, ATTN_NORM);
  443.       }
  444.    if ( self.goalentity.spawnflags & ANGLES )
  445.       {
  446.       self.rotate = '0 0 0';
  447.       self.angles = self.finalangle;
  448.       }
  449.    if ( self.goalentity.spawnflags & NO_ROTATE )
  450.       {
  451.       self.rotate = '0 0 0';
  452.       }
  453.    self.endtime = self.ltime + self.goalentity.wait;
  454.    self.think1 = rotate_train_next;
  455.    };
  456.  
  457. void() rotate_train_stop =
  458.     {
  459.    self.state = STATE_STOP;
  460.  
  461.    if ( self.goalentity.noise )
  462.       {
  463.       sound (self, CHAN_VOICE, self.goalentity.noise, 1, ATTN_NORM);
  464.       }
  465.    else
  466.       {
  467.       sound (self, CHAN_VOICE, self.noise, 1, ATTN_NORM);
  468.       }
  469.    if ( self.goalentity.spawnflags & ANGLES )
  470.       {
  471.       self.rotate = '0 0 0';
  472.       self.angles = self.finalangle;
  473.       }
  474.    if ( self.goalentity.spawnflags & NO_ROTATE )
  475.       {
  476.       self.rotate = '0 0 0';
  477.       }
  478.  
  479.    self.dmg = 0;
  480.    self.think1 = rotate_train_next;
  481.    };
  482.  
  483. void() rotate_train_next =
  484. {
  485.    local entity targ;
  486.    local entity current;
  487.    local vector   vdestdelta;
  488.    local float    len, traveltime, div;
  489.    local string temp;
  490.  
  491.    self.state = STATE_NEXT;
  492.  
  493.    current = self.goalentity;
  494.    targ = find (world, targetname, self.path );
  495.    if ( targ.classname != "path_rotate" )
  496.       objerror( "Next target is not path_rotate" );
  497.  
  498.    if ( self.goalentity.noise1 )
  499.       {
  500.       self.noise1 = self.goalentity.noise1;
  501.       }
  502.    sound (self, CHAN_VOICE, self.noise1, 1, ATTN_NORM);
  503.  
  504.    self.goalentity = targ;
  505.    self.path = targ.target;
  506.    if (!self.path )
  507.       objerror ("rotate_train_next: no next target");
  508.  
  509.    if ( targ.spawnflags & STOP )
  510.       {
  511.       self.think1 = rotate_train_stop;
  512.       }
  513.    else if (targ.wait)
  514.         {
  515.       self.think1 = rotate_train_wait;
  516.       }
  517.     else
  518.         {
  519.       self.think1 = rotate_train_next;
  520.         }
  521.  
  522.    if ( current.event )
  523.       {
  524.       // Trigger any events that should happen at the corner.
  525.       temp = self.target;
  526.       self.target = current.event;
  527.       self.message = current.message;
  528.       SUB_UseTargets();
  529.       self.target = temp;
  530.       self.message = string_null;
  531.       }
  532.  
  533.    if ( current.spawnflags & ANGLES )
  534.       {
  535.       self.rotate = '0 0 0';
  536.       self.angles = self.finalangle;
  537.       }
  538.  
  539.    if ( current.spawnflags & ROTATION )
  540.       {
  541.       self.rotate = current.rotate;
  542.       }
  543.  
  544.    if ( current.spawnflags & DAMAGE )
  545.       {
  546.       self.dmg = current.dmg;
  547.       }
  548.  
  549.    if ( current.spawnflags & SET_DAMAGE )
  550.       {
  551.       SetDamageOnTargets( current.dmg );
  552.       }
  553.  
  554.    if ( current.speed == -1 )
  555.         {
  556.         // Warp to the next path_corner
  557.       setorigin( self, targ.origin );
  558.       self.endtime = self.ltime + 0.01;
  559.       SetTargetOrigin();
  560.  
  561.       if ( targ.spawnflags & ANGLES )
  562.          {
  563.          self.angles = targ.angles;
  564.          }
  565.  
  566.       self.duration = 1;         // 1 / duration
  567.       self.cnt = time;           // start time
  568.       self.dest2 = '0 0 0';      // delta
  569.       self.dest1 = self.origin;  // original position
  570.       self.finaldest = self.origin;
  571.       }
  572.     else
  573.         {
  574.       self.state = STATE_MOVE;
  575.  
  576.       self.finaldest = targ.origin;
  577.       if (self.finaldest == self.origin)
  578.          {
  579.          self.velocity = '0 0 0';
  580.          self.endtime = self.ltime + 0.1;
  581.  
  582.          self.duration = 1;        // 1 / duration
  583.          self.cnt = time;          // start time
  584.          self.dest2 = '0 0 0';     // delta
  585.          self.dest1 = self.origin; // original position
  586.          self.finaldest = self.origin;
  587.          return;
  588.          }
  589.       // set destdelta to the vector needed to move
  590.       vdestdelta = self.finaldest - self.origin;
  591.  
  592.       // calculate length of vector
  593.       len = vlen (vdestdelta);
  594.  
  595.       if ( current.spawnflags & MOVETIME )
  596.          {
  597.          traveltime = current.speed;
  598.          }
  599.       else
  600.          {
  601.          // check if there's a speed change
  602.          if (current.speed>0)
  603.             self.speed = current.speed;
  604.  
  605.          if (!self.speed)
  606.             objerror("No speed is defined!");
  607.  
  608.          // divide by speed to get time to reach dest
  609.          traveltime = len / self.speed;
  610.          }
  611.  
  612.       if (traveltime < 0.1)
  613.          {
  614.          self.velocity = '0 0 0';
  615.          self.endtime = self.ltime + 0.1;
  616.          if ( targ.spawnflags & ANGLES )
  617.             {
  618.             self.angles = targ.angles;
  619.             }
  620.          return;
  621.          }
  622.  
  623.       // qcc won't take vec/float
  624.       div = 1 / traveltime;
  625.  
  626.       if ( targ.spawnflags & ANGLES )
  627.          {
  628.          self.finalangle = SUB_NormalizeAngles( targ.angles );
  629.          self.rotate = ( targ.angles - self.angles ) * div;
  630.          }
  631.  
  632.       // set endtime to trigger a think when dest is reached
  633.       self.endtime = self.ltime + traveltime;
  634.  
  635.       // scale the destdelta vector by the time spent traveling to get velocity
  636.       self.velocity = vdestdelta * div;
  637.  
  638.       self.duration = div;      // 1 / duration
  639.       self.cnt = time;          // start time
  640.       self.dest2 = vdestdelta;  // delta
  641.       self.dest1 = self.origin; // original position
  642.       }
  643.    };
  644.  
  645. void() rotate_train_find =
  646.    {
  647.    local entity targ;
  648.  
  649.    self.state = STATE_FIND;
  650.  
  651.    LinkRotateTargets();
  652.  
  653.    // the first target is the point of rotation.
  654.    // the second target is the path.
  655.    targ = find ( world, targetname, self.path);
  656.    if ( targ.classname != "path_rotate" )
  657.       objerror( "Next target is not path_rotate" );
  658.  
  659.    // Save the current entity
  660.    self.goalentity = targ;
  661.  
  662.    if ( targ.spawnflags & ANGLES )
  663.       {
  664.       self.angles = targ.angles;
  665.       self.finalangle = SUB_NormalizeAngles( targ.angles );
  666.       }
  667.  
  668.    self.path = targ.target;
  669.    setorigin (self, targ.origin );
  670.    SetTargetOrigin();
  671.    RotateTargetsFinal();
  672.    self.think1 = rotate_train_next;
  673.    if (!self.targetname)
  674.       {
  675.       // not triggered, so start immediately
  676.       self.endtime = self.ltime + 0.1;
  677.       }
  678.    else
  679.       {
  680.       self.endtime = 0;
  681.       }
  682.  
  683.    self.duration = 1;        // 1 / duration
  684.    self.cnt = time;          // start time
  685.    self.dest2 = '0 0 0';     // delta
  686.    self.dest1 = self.origin; // original position
  687.    };
  688.  
  689. /*QUAKED func_rotate_train (0 .5 .8) (-8 -8 -8) (8 8 8)
  690. In path_rotate, set speed to be the new speed of the train after it reaches
  691. the path change.  If speed is -1, the train will warp directly to the next
  692. path change after the specified wait time.  If MOVETIME is set on the
  693. path_rotate, the train to interprets "speed" as the length of time to
  694. take moving from one corner to another.
  695.  
  696. "noise" contains the name of the sound to play when train stops.
  697. "noise1" contains the name of the sound to play when train moves.
  698. Both "noise" and "noise1" defaults depend upon "sounds" variable and
  699. can be overridden by the "noise" and "noise1" variable in path_rotate.
  700.  
  701. Also in path_rotate, if STOP is set, the train will wait until it is
  702. retriggered before moving on to the next goal.
  703.  
  704. Trains are moving platforms that players can ride.
  705. "path" specifies the first path_rotate and is the starting position.
  706. If the train is the target of a button or trigger, it will not begin moving until activated.
  707. The func_rotate_train entity is the center of rotation of all objects targeted by it.
  708.  
  709. If "deathtype" is set with a string, this is the message that will appear when a player is killed by the train.
  710.  
  711. speed    default 100
  712. dmg      default  0
  713. sounds
  714. 1) ratchet metal
  715. */
  716.  
  717. void() rotate_train =
  718.    {
  719.    objerror ("rotate_train entities should be changed to rotate_object with\nfunc_rotate_train controllers\n");
  720.    };
  721.  
  722. void() func_rotate_train =
  723.    {
  724.    if (!self.speed)
  725.         self.speed = 100;
  726.     if (!self.target)
  727.       objerror ("rotate_train without a target");
  728.  
  729.    if ( !self.noise )
  730.       {
  731.       if (self.sounds == 0)
  732.          {
  733.          self.noise = ("misc/null.wav");
  734.          }
  735.  
  736.       if (self.sounds == 1)
  737.          {
  738.          self.noise = ("plats/train2.wav");
  739.          }
  740.       }
  741.    if ( !self.noise1 )
  742.       {
  743.       if (self.sounds == 0)
  744.          {
  745.          self.noise1 = ("misc/null.wav");
  746.          }
  747.       if (self.sounds == 1)
  748.          {
  749.          self.noise1 = ("plats/train1.wav");
  750.          }
  751.       }
  752.  
  753.    precache_sound( self.noise );
  754.    precache_sound( self.noise1 );
  755.  
  756.    self.cnt = 1;
  757.    self.solid    = SOLID_NOT;
  758.    self.movetype = MOVETYPE_STEP;
  759.    self.use = rotate_train_use;
  760.  
  761.    setmodel (self, self.model);
  762.    setsize (self, self.mins, self.maxs);
  763.     setorigin (self, self.origin);
  764.  
  765. // start trains on the second frame, to make sure their targets have had
  766. // a chance to spawn
  767.    self.ltime = time;
  768.     self.nextthink = self.ltime + 0.1;
  769.    self.endtime = self.ltime + 0.1;
  770.    self.think = rotate_train_think;
  771.    self.think1 = rotate_train_find;
  772.    self.state = STATE_FIND;
  773.  
  774.    self.duration = 1;        // 1 / duration
  775.    self.cnt = 0.1;           // start time
  776.    self.dest2 = '0 0 0';     // delta
  777.    self.dest1 = self.origin; // original position
  778.  
  779.  
  780.    self.flags = self.flags | FL_ONGROUND;
  781.    };
  782.  
  783. //************************************************
  784. //
  785. // Moving clip walls
  786. //
  787. //************************************************
  788.  
  789. void() rotate_door_reversedirection;
  790. void() rotate_door_group_reversedirection;
  791.  
  792. void() movewall_touch =
  793.    {
  794.    if (time < self.owner.attack_finished)
  795.         return;
  796.  
  797.    if ( self.dmg )
  798.       {
  799.       T_Damage (other, self, self.owner, self.dmg);
  800.       self.owner.attack_finished = time + 0.5;
  801.       }
  802.    else if ( self.owner.dmg )
  803.       {
  804.       T_Damage (other, self, self.owner, self.owner.dmg);
  805.       self.owner.attack_finished = time + 0.5;
  806.       }
  807.    };
  808.  
  809. void() movewall_blocked =
  810.    {
  811.    local entity temp;
  812.  
  813.    if (time < self.owner.attack_finished)
  814.         return;
  815.  
  816.    self.owner.attack_finished = time + 0.5;
  817.  
  818.    if ( self.owner.classname == "func_rotate_door" )
  819.       {
  820.       temp = self;
  821.       self = self.owner;
  822.       rotate_door_group_reversedirection();
  823.       self = temp;
  824.       }
  825.  
  826.    if ( self.dmg )
  827.       {
  828.       T_Damage (other, self, self.owner, self.dmg);
  829.       self.owner.attack_finished = time + 0.5;
  830.       }
  831.    else if ( self.owner.dmg )
  832.       {
  833.       T_Damage (other, self, self.owner, self.owner.dmg);
  834.       self.owner.attack_finished = time + 0.5;
  835.       }
  836.    };
  837.  
  838. void() movewall_think =
  839.    {
  840.    self.ltime = time;
  841.    self.nextthink = time + 0.02;
  842.    };
  843.  
  844. /*QUAKED func_movewall (0 .5 .8) ? VISIBLE TOUCH NONBLOCKING
  845. Used to emulate collision on rotating objects.
  846.  
  847. VISIBLE causes brush to be displayed.
  848.  
  849. TOUCH specifies whether to cause damage when touched by player.
  850.  
  851. NONBLOCKING makes the brush non-solid.  This is useless if VISIBLE is set.
  852.  
  853. "dmg" specifies the damage to cause when touched or blocked.
  854. */
  855. void() func_movewall =
  856.    {
  857.    self.angles = '0 0 0';
  858.    self.movetype = MOVETYPE_PUSH;
  859.    if ( self.spawnflags & NONBLOCKING )
  860.       {
  861.       self.solid = SOLID_NOT;
  862.       }
  863.    else
  864.       {
  865.       self.solid = SOLID_BSP;
  866.       self.blocked = movewall_blocked;
  867.       }
  868.    if ( self.spawnflags & TOUCH )
  869.       {
  870.       self.touch = movewall_touch;
  871.       }
  872.    setmodel (self,self.model);
  873.    if ( !( self.spawnflags & VISIBLE ) )
  874.       {
  875.       self.model = string_null;
  876.       }
  877.    self.think = movewall_think;
  878.    self.nextthink = time + 0.02;
  879.    self.ltime = time;
  880.    };
  881.  
  882. /*QUAKED rotate_object (0 .5 .8) ?
  883. This defines an object to be rotated.  Used as the target of func_rotate_door.
  884. */
  885. void() rotate_object =
  886.    {
  887.    self.classname = "rotate_object";
  888.    self.solid = SOLID_NOT;
  889.    self.movetype = MOVETYPE_NONE;
  890.    setmodel (self,self.model);
  891.    setsize( self, self.mins, self.maxs );
  892.    self.think = SUB_Null;
  893.    };
  894.  
  895. //************************************************
  896. //
  897. // Rotating doors
  898. //
  899. //************************************************
  900.  
  901. void() rotate_door_think2 =
  902.    {
  903.    local float t;
  904.  
  905.    t = time - self.ltime;
  906.    self.ltime = time;
  907.  
  908.    // change to alternate textures
  909.    self.frame = 1 - self.frame;
  910.  
  911.    self.angles = self.dest;
  912.  
  913.    if ( self.state == STATE_OPENING )
  914.       {
  915.       self.state = STATE_OPEN;
  916.       }
  917.    else
  918.       {
  919.       if ( self.spawnflags & STAYOPEN )
  920.          {
  921.          rotate_door_group_reversedirection();
  922.          return;
  923.          }
  924.       self.state = STATE_CLOSED;
  925.       }
  926.  
  927.    sound(self, CHAN_VOICE, self.noise3, 1, ATTN_NORM);
  928.    self.think = SUB_Null;
  929.  
  930.    RotateTargetsFinal();
  931.    };
  932.  
  933. void() rotate_door_think =
  934.    {
  935.    local float t;
  936.  
  937.    t = time - self.ltime;
  938.    self.ltime = time;
  939.  
  940.    if ( time < self.endtime )
  941.       {
  942.       self.angles = self.angles + ( self.rotate * t );
  943.       RotateTargets();
  944.       }
  945.    else
  946.       {
  947.       self.angles = self.dest;
  948.       RotateTargets();
  949.       self.think = rotate_door_think2;
  950.       }
  951.  
  952.    self.nextthink = time + 0.01;
  953.    };
  954.  
  955. void() rotate_door_reversedirection =
  956.    {
  957.    local vector start;
  958.  
  959.    // change to alternate textures
  960.     self.frame = 1 - self.frame;
  961.  
  962.    if ( self.state == STATE_CLOSING )
  963.       {
  964.       start = self.dest1;
  965.       self.dest = self.dest2;
  966.       self.state = STATE_OPENING;
  967.       }
  968.    else
  969.       {
  970.       start = self.dest2;
  971.       self.dest = self.dest1;
  972.       self.state = STATE_CLOSING;
  973.       }
  974.  
  975.    sound(self, CHAN_VOICE, self.noise2, 1, ATTN_NORM);
  976.  
  977.    self.rotate = ( self.dest - start ) * ( 1 / self.speed );
  978.    self.think = rotate_door_think;
  979.    self.nextthink = time + 0.02;
  980.    self.endtime = time + self.speed - ( self.endtime - time );
  981.    self.ltime = time;
  982.    };
  983.  
  984. void() rotate_door_group_reversedirection =
  985.    {
  986.    local string name;
  987.  
  988.    // tell all associated rotaters to reverse direction
  989.    if ( self.group )
  990.       {
  991.       name = self.group;
  992.       self = find( world, group, name);
  993.       while( self )
  994.          {
  995.          rotate_door_reversedirection();
  996.          self = find( self, group, name);
  997.          }
  998.       }
  999.    else
  1000.       {
  1001.       rotate_door_reversedirection();
  1002.       }
  1003.    };
  1004.  
  1005. void() rotate_door_use =
  1006.    {
  1007.    local entity t;
  1008.    local vector start;
  1009.  
  1010.    if ( ( self.state != STATE_OPEN ) && ( self.state != STATE_CLOSED ) )
  1011.       return;
  1012.  
  1013.    if ( !self.cnt )
  1014.       {
  1015.       self.cnt = 1;
  1016.       LinkRotateTargets();
  1017.       }
  1018.  
  1019.    // change to alternate textures
  1020.     self.frame = 1 - self.frame;
  1021.  
  1022.    if ( self.state == STATE_CLOSED )
  1023.       {
  1024.       start = self.dest1;
  1025.       self.dest = self.dest2;
  1026.       self.state = STATE_OPENING;
  1027.       }
  1028.    else
  1029.       {
  1030.       start = self.dest2;
  1031.       self.dest = self.dest1;
  1032.       self.state = STATE_CLOSING;
  1033.       }
  1034.  
  1035.    sound(self, CHAN_VOICE, self.noise2, 1, ATTN_NORM);
  1036.  
  1037.    self.rotate = ( self.dest - start ) * ( 1 / self.speed );
  1038.    self.think = rotate_door_think;
  1039.    self.nextthink = time + 0.01;
  1040.    self.endtime = time + self.speed;
  1041.    self.ltime = time;
  1042.    };
  1043.  
  1044.  
  1045. /*QUAKED func_rotate_door (0 .5 .8) (-8 -8 -8) (8 8 8) STAYOPEN
  1046. Creates a door that rotates between two positions around a point of
  1047. rotation each time it's triggered.
  1048.  
  1049. STAYOPEN tells the door to reopen after closing.  This prevents a trigger-
  1050. once door from closing again when it's blocked.
  1051.  
  1052. "dmg" specifies the damage to cause when blocked.  Defaults to 2.  Negative numbers indicate no damage.
  1053. "speed" specifies how the time it takes to rotate
  1054.  
  1055. "sounds"
  1056. 1) medieval (default)
  1057. 2) metal
  1058. 3) base
  1059. */
  1060.  
  1061. void() func_rotate_door =
  1062.    {
  1063.    if (!self.target)
  1064.       {
  1065.       objerror( "rotate_door without target." );
  1066.       }
  1067.  
  1068.    self.dest1 = '0 0 0';
  1069.    self.dest2 = self.angles;
  1070.    self.angles = self.dest1;
  1071.  
  1072.    // default to 2 seconds
  1073.    if ( !self.speed )
  1074.       {
  1075.       self.speed = 2;
  1076.       }
  1077.  
  1078.    self.cnt = 0;
  1079.  
  1080.    if (!self.dmg)
  1081.         self.dmg = 2;
  1082.    else if ( self.dmg < 0 )
  1083.       {
  1084.       self.dmg = 0;
  1085.       }
  1086.  
  1087.    if (self.sounds == 0)
  1088.       self.sounds = 1;
  1089.     if (self.sounds == 1)
  1090.     {
  1091.         precache_sound ("doors/latch2.wav");
  1092.         precache_sound ("doors/winch2.wav");
  1093.         precache_sound ("doors/drclos4.wav");
  1094.         self.noise1 = "doors/latch2.wav";
  1095.         self.noise2 = "doors/winch2.wav";
  1096.         self.noise3 = "doors/drclos4.wav";
  1097.     }
  1098.     if (self.sounds == 2)
  1099.     {
  1100.         precache_sound ("doors/airdoor1.wav");
  1101.         precache_sound ("doors/airdoor2.wav");
  1102.         self.noise2 = "doors/airdoor1.wav";
  1103.         self.noise1 = "doors/airdoor2.wav";
  1104.         self.noise3 = "doors/airdoor2.wav";
  1105.     }
  1106.     if (self.sounds == 3)
  1107.     {
  1108.         precache_sound ("doors/basesec1.wav");
  1109.         precache_sound ("doors/basesec2.wav");
  1110.         self.noise2 = "doors/basesec1.wav";
  1111.         self.noise1 = "doors/basesec2.wav";
  1112.         self.noise3 = "doors/basesec2.wav";
  1113.     }
  1114.  
  1115.    self.solid = SOLID_NOT;
  1116.    self.movetype = MOVETYPE_NONE;
  1117.    setmodel (self, self.model);
  1118.    setorigin( self, self.origin );
  1119.    setsize( self, self.mins, self.maxs );
  1120.    self.state = STATE_CLOSED;
  1121.    self.use = rotate_door_use;
  1122.    self.think = SUB_Null;
  1123.    };
  1124.